﻿using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WebAssertTestLibrary.Core;

namespace WebAssertTestLibrary.MSTest
{
    public static class WebAssert
    {
        /// <summary>
        /// Asserts whether the resource being served by the passed ASP.NET server instance & path is compliant HTML
        /// </summary>
        /// <param name="testContext">The TestContext for the test class.</param>
        /// <param name="path">The path of the resource to check.</param>
        /// <example>WebAssert.ReturnsValidHtml(TestContext, "Default.aspx");</example>
        public static void ReturnsValidHtml(TestContext testContext, string path)
        {
            var url = GetAspNetServerUrl(testContext, path);
            ReturnsValidHtml(url);
        }
        
        static string GetAspNetServerUrl(TestContext testContext, string path)
        {
            var key = testContext.Properties.Keys
                .OfType<string>()
                .FirstOrDefault(k => k.StartsWith(TestContext.AspNetDevelopmentServerPrefix));
            
            if (key == null)
                throw new InvalidOperationException("You must specify the AspNetDevelopmentServer attribute on your test method.");

            var url = String.Format("{0}{1}",
                testContext.Properties[key],
                path);
            return url;
        }

        /// <summary>
        /// Asserts wheter the resource being served at the passed url is compliant HTML
        /// </summary>
        /// <param name="url">The url of the resource to test for HTML compliance.</param>
        public static void ReturnsValidHtml(string url)
        {
            var validator = new XhtmlValidator();
            var result = validator.ReturnsValidHtml(url);
            if (!result.IsValid) Fail(result);
        }

        /// <summary>
        /// Asserts whether the passed HTML is compliant
        /// </summary>
        /// <param name="html">The HTML to validate.</param>
        public static void IsValidHtml(string html)
        {
            IsValidHtml(html, null);
        }

        /// <summary>
        /// Asserts whether the passed HTML is compliant
        /// </summary>
        /// <param name="html">The HTML to validate.</param>
        /// <param name="validatorUrl">The URL to the hosted instance of the Markup validator.</param>
        public static void IsValidHtml(string html, string validatorUrl)
        {
            var validator = new XhtmlValidator();
            validator.ValidatorUrl = validatorUrl ?? validator.ValidatorUrl;
            var result = validator.IsValidHtml(html);
            if (!result.IsValid)
                Fail(result);
        }

        /// <summary>
        /// Asserts whether the passed CSS is compliant
        /// </summary>
        /// <param name="css"></param>
        public static void IsValidCss(string css)
        {
            IsValidCss(css, null);
        }

        /// <summary>
        /// Asserts whether the passed CSS is compliant
        /// </summary>
        /// <param name="css">The CSS text to validate.</param>
        /// <param name="validatorUrl">The URL to the hosted instance of the CSS validator.</param>
        public static void IsValidCss(string css, string validatorUrl)
        {
            var validator = new CssValidator();
            validator.ValidatorUrl = validatorUrl ?? validator.ValidatorUrl;
            var result = validator.IsValidCss(css);
            if (!result.IsValid)
                Fail(result);
        }

        // ResponseBodyContains
        // ResponseHeaderContains
        // ReturnsWithDoctype(Doctype)  Doctype.Html5, etc.

        private static void Fail(W3CValidityCheckResult result)
        {
            var errors = result.ErrorsCount == 1 ? "error" : "errors";
            var warnings = result.WarningsCount == 1 ? "warning" : "warnings";

            throw new AssertFailedException(
                String.Format("The passed URL returned invalid HTML: {0} {1}, {2} {3}",
                    result.ErrorsCount, errors, result.WarningsCount, warnings)
            );
        }
    }
}